/*
 * @(#)MessageDigest.java	1.35 01/12/12
 *
 * 版权所有2002 Sun MicroSystems，Inc.保留所有权利。Sun专有/机密。使用受许可条款的约束。
 */

package java.security;

import java.util.*;
import java.lang.*;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.InputStream;
import java.io.ByteArrayInputStream;

/**
 * 此MessageDigest类提供消息摘要算法的功能，如MD5或SHA。消息摘要是安全的单向散列函数，可以获取任意大小的数据并输出固定长度的散列值。
 *
 * 与Java Security中的其他基于算法的类一样，MessageDigest有两个主要组件：
 *
 * <dl>
 *
 * <dt><b>Message Digest API</b> (Application Program Interface)
 *
 * 这是需要消息摘要服务的应用程序调用的方法的接口。该API由所有公共方法组成。
 *
 * <dt><b>Message Digest SPI</b> (Service Provider Interface)
 *
 * 这是由提供特定算法的提供程序实现的接口。它由名称以<em>引擎</em>为前缀的所有方法组成。
 * 每个这样的方法都由一个相应命名的公共API方法调用。
 * 例如，<code>Engineering Reset</code>方法由<code>Reset</code>方法调用。
 * SPI方法是抽象的；提供者必须提供具体的实现。
 *
 * </dl>
 *
 * <p>MessageDigest对象启动时已初始化。使用<a href>更新</a>方法通过它来处理数据。
 * 在任何时候都可以调用<a href>Reset</a>来重置摘要。
 * 一旦更新了所有要更新的数据，就应该调用一个<a href>digest</a>方法来完成散列计算。
 *
 * <p><code>Digest</code>方法可以针对给定的更新次数调用一次。
 * 调用<code>Digest</code>后，MessageDigest对象被重置为其初始化状态。
 *
 * <p>实现可以自由实现Cloneable接口，这样做将允许客户端应用程序在克隆之前使用<code>cloneable</code>测试可克隆能力：<p>
 *
 * <pre>
 * MessageDigest md = MessageDigest.getInstance("SHA");
 *
 * if (md instanceof Cloneable) {
 *     md.update(toChapter1);
 *     MessageDigest tc1 = md.clone();
 *     byte[] toChapter1Digest = tc1.digest;
 *     md.update(toChapter2);
 *     ...etc.
 * } else {
 *     throw new DigestException("couldn't make digest of partial content");
 * }
 * </pre>
 *
 * 请注意，如果给定的实现不可克隆，则仍然可以通过实例化几个实例来计算中间摘要，前提是预先知道摘要的数量。
 *
 * @see DigestInputStream
 * @see DigestOutputStream
 *
 * @version 1.31 97/02/03
 * @author Benjamin Renaud 
 */
public abstract class MessageDigest {

    /* Are we in debugging mode? */
    private static boolean debug = false;

    /* The digest bits, if any. */
    private byte[] digestBits;

    private String algorithm;

    /**
     * Creates a message digest with the specified algorithm name.
     * 
     * @param algorithm 摘要算法的标准名称。有关标准算法名称的信息，请参阅<a href>Java加密体系结构API规范和参考中的附录A。
     */
    protected MessageDigest(String algorithm) {
	this.algorithm = algorithm;
    }

    /**
     * 生成实现指定摘要算法的MessageDigest对象。如果默认提供程序包包含实现算法的MessageDigest子类，则返回该子类的实例。
	 * 如果该算法在默认程序包中不可用，则搜索其他程序包。
     *
     * @param algorithm 请求的算法的名称。有关标准算法名称的信息，请参阅<a href>Java加密体系结构API规范和参考</a>中的附录A。
     *
     * @return a Message Digest object implementing the specified
     * algorithm.
     *
     * @exception NoSuchAlgorithmException if the algorithm is
     * not available in the caller's environment.  
     */
    public static MessageDigest getInstance(String algorithm) 
    throws NoSuchAlgorithmException { 
	try {
	    return (MessageDigest)Security.getImpl(algorithm, 
						   "MessageDigest", null);
	} catch(NoSuchProviderException e) {
	    throw new InternalError("please send a bug report via " +
				    System.getProperty("java.vendor.url.bug"));
	}
    }

    /**
     * 生成一个MessageDigest对象，该对象实现由指定提供程序提供的指定算法(如果该提供程序提供了这样的算法)。
     *
     * @param algorithm 请求的算法的名称。有关标准算法名称的信息，请参阅<a href>Java加密体系结构API规范和参考中的附录A。
     *
     * @param provider the name of the provider.
     *
     * @return a Message Digest object implementing the specified
     * algorithm.
     *
     * @exception NoSuchAlgorithmException 如果该算法在所请求的提供程序提供的包中不可用。
     *
     * @exception NoSuchProviderException if the provider is not
     * available in the environment. 
     * 
     * @see Provider 
     */
    public static MessageDigest getInstance(String algorithm, String provider)
    throws NoSuchAlgorithmException, NoSuchProviderException {
	return (MessageDigest)Security.getImpl(algorithm, 
					       "MessageDigest", provider);
    }

    /**
     * Updates the digest using the specified byte.    
     * 
     * @param input the byte with which to update the digest.
     */
    public void update(byte input) {
	engineUpdate(input);
    }


    /**
     * 使用指定的字节数组更新摘要，从指定的偏移量开始。
     * 
     * @param input the array of bytes.
     *
     * @param offset the offset to start from in the array of bytes.
     *
     * @param len the number of bytes to use, starting at 
     * <code>offset</code>.  
     */
    public void update(byte[] input, int offset, int len) {
	engineUpdate(input, offset, len);
    }

    /**
     * Updates the digest using the specified array of bytes.
     * 
     * @param input the array of bytes.
     */
    public void update(byte[] input) {
	engineUpdate(input, 0, input.length);
    }

    /**
     * 通过执行最终操作(如填充)来完成哈希计算。进行此调用后，摘要会被重置。
     *
     * @return the array of bytes for the resulting hash value.  
     */
    public byte[] digest() {
	/* Resetting is the responsibility of implementors. */
	digestBits = engineDigest();
	return digestBits;
    }

    /**
     * 使用指定的字节数组对摘要执行最终更新，然后完成摘要计算。
	 * 也就是说，该方法首先对数组调用<a href>update</a>，然后调用<a href>Digest()</a>。
     *
     * @param input the input to be updated before the digest is
     * completed.
     *
     * @return the array of bytes for the resulting hash value.  
     */
    public byte[] digest(byte[] input) {
	update(input);
	return digest();
    }

    /**
     * Helper function that prints unsigned two character hex digits.
     */
    private static void hexDigit(PrintStream p, byte x) {
	char c;
	
	c = (char) ((x >> 4) & 0xf);
	if (c > 9) {
	    c = (char) ((c - 10) + 'a');
	} else {
	    c = (char) (c + '0');
	}
	p.write(c);

	c = (char) (x & 0xf);
	if (c > 9) {
	    c = (char)((c - 10) + 'a');
	} else {
	    c = (char)(c + '0');
	}
	p.write(c);
    }

    /**
     * Returns a string representation of this message digest object.  
     */
    public String toString() {
	ByteArrayOutputStream ou = new ByteArrayOutputStream();
	PrintStream p = new PrintStream(ou);
		
	p.print(this.getClass().getName()+" Message Digest ");
	if (digestBits != null) {
	    p.print("<");
	    for(int i = 0; i < digestBits.length; i++)
 	        hexDigit(p, digestBits[i]);
	    p.print(">");
	} else {
	    p.print("<incomplete>");
	}
	p.println();
	return (ou.toString());
    }

    /**
     * Compares two digests for equality. Does a simple byte compare.
     * 
     * @param digesta one of the digests to compare.
     * 
     * @param digestb the other digest to compare.    
     *
     * @return true if the digests are equal, false otherwise.
     */
    public static boolean isEqual(byte digesta[], byte digestb[]) {
	int	i;
		
	if (digesta.length != digestb.length)
	    return false;

	for (i = 0; i < digesta.length; i++) {
	    if (digesta[i] != digestb[i]) {
		return false;
	    }
	}
	return true;
    }

    /**
     * Resets the digest for further use.
     */
    public void reset() {
	engineReset();
	digestBits = null;
    }

    /** 
     * 返回一个独立于实现详细信息的标识算法的字符串。该名称应该是标准的Java安全名称(如“SHA”、“MD5”等)。
	 * 有关标准算法名称的信息，请参阅<a href>Java加密体系结构API规范和参考中的附录A。
     */
    public final String getAlgorithm() {
	return algorithm;
    }

    /**
     * <b>SPI</b>: Updates the digest using the specified byte.
     *
     * @param input the byte to use for the update.
     */
    protected abstract void engineUpdate(byte input);

    /**
     * <b>SPI</b>：使用指定的字节数组更新摘要，从指定的偏移量开始。如果摘要已经敲定，这应该是一个禁止操作。
     *
     * @param input the array of bytes to use for the update.
     *
     * @param offset the offset to start from in the array of bytes.
     *
     * @param len the number of bytes to use, starting at 
     * <code>offset</code>.
     */
    protected abstract void engineUpdate(byte[] input, int offset, int len);

    /**
     * <b>SPI</b>：通过执行填充等最终操作来完成散列计算。一旦调用了<code>Engineering Digest</code>，
	 * 就应该重置引擎(请参见<a href>Reset</a>)。重置是引擎实现者的责任。
     *
     * @return the array of bytes for the resulting hash value.  
     */
    protected abstract byte[] engineDigest();

    /**
     * <b>SPI</b>: Resets the digest for further use.
     */
    protected abstract void engineReset();    


    /**    
     * Returns a clone if the implementation is cloneable.    
     * 
     * @return a clone if the implementation is cloneable.
     *
     * @exception CloneNotSupportedException if this is called on an
     * implementation that does not support <code>Cloneable</code>.
     */
    public Object clone() throws CloneNotSupportedException {
	if (this instanceof Cloneable) {
	    return super.clone();
	} else {
	    throw new CloneNotSupportedException();
	}
    }
  

    private void debug(String statement) {
	if (debug) {
	    System.err.println(statement);
	}
    }


}
